package com.brightlight.dreamcrafter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.brightlight.dreamcrafter.common.util.IDUtil;
import com.brightlight.dreamcrafter.common.util.MinioUtil;
import com.brightlight.dreamcrafter.mapper.UploadChunkMapper;
import com.brightlight.dreamcrafter.model.dto.UploadFileDTO;
import com.brightlight.dreamcrafter.model.entity.UploadFileEntity;
import com.brightlight.dreamcrafter.service.IFileUploadService;
import com.google.common.collect.Maps;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.Map;

import static com.brightlight.dreamcrafter.common.constant.CommonConstant.LINK_FILE_PATH;

/**
 * @description: 文件上传接口
 * @author: LiMG
 * @create: 2024-04-16 09:45:50
 **/
@Service
public class FileUploadServiceImpl implements IFileUploadService {

    @Resource
    private MinioUtil minioUtil;

    @Resource
    private UploadChunkMapper uploadChunkMapper;

    /**
     * 校验文件的MD5值，判断文件是否存在
     *
     * @param md5
     * @return
     */
    @Override
    public Boolean checkFileMd5(String md5) {
        int count = uploadChunkMapper.getFileExist(md5);
        return count > 0;
    }

    /**
     * 获取上传分片的索引
     *
     * @param md5
     * @param bucketName
     * @return
     * @throws Exception
     */
    @Override
    public Integer getUploadChunkIndex(String md5, String bucketName) throws Exception {
        return uploadChunkMapper.getUploadChunkIndex(md5, bucketName);
    }

    /**
     * 分片文件上传
     *
     * @param uploadChunk
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> uploadByChunk(UploadFileDTO uploadChunk) throws Exception {
        String bucketName = uploadChunk.getBucketName();
        // 如果桶不存在，则创建桶
        if (!minioUtil.bucketExists(bucketName)) {
            minioUtil.makeBucket(bucketName);
        }
        // 获取文件对象名称
        String objectName = minioUtil.getChunkObjectName(uploadChunk.getFileName(), uploadChunk.getMd5());
        // 文件对象
        MultipartFile file = uploadChunk.getFile();
        // 当前文件分片索引
        int chunk = uploadChunk.getChunk();
        // 文件总分片数
        int chunks = uploadChunk.getChunks();
        // 文件MD5值
        String md5 = uploadChunk.getMd5();
        UploadFileEntity entity = uploadChunkMapper.selectOne(new QueryWrapper<UploadFileEntity>().eq("md5", md5), false);
        // 数据记录ID
        String id;
        if (null == entity) {
            // 不存在，插入当前索引的数据
            id = IDUtil.id();
            entity = new UploadFileEntity();
            entity.setId(id);
            entity.setFileName(uploadChunk.getFileName());
            entity.setChunkSize(chunks);
            entity.setChunkIndex(chunk);
            entity.setMd5(md5);
            entity.setBucketName(bucketName);
            entity.setTime();
            uploadChunkMapper.insert(entity);
        } else {
            id = entity.getId();
            entity.setChunkIndex(chunk);
            entity.setUpdateTime();
            uploadChunkMapper.update(entity, new QueryWrapper<UploadFileEntity>().eq("id", entity.getId()));
        }
        // 上传分片文件
        boolean flag = minioUtil.uploadChunk(bucketName, objectName, chunk, file);
        Map<String, Object> map = Maps.newHashMap();
        map.put("objectName", objectName);
        if (!flag) return null;
        if (chunk == chunks - 1) {
            // 设置最终的对象名
            String finalObjectName = objectName.replace(md5, String.valueOf(IDUtil.id()));
            // 当最后一片文件上传成功后，进行文件合并
            boolean result = minioUtil.mergeFiles(bucketName, objectName, finalObjectName, chunks);
            if (result) {
                // 文件合并完成表示文件上传完成，更新文件上传完成状态为已完成
                UploadFileEntity update = new UploadFileEntity();
                update.setId(id);
                update.setFinishState(1);
                update.setSavePath(finalObjectName);
                update.setUpdateTime();
                uploadChunkMapper.updateById(update);
                map.put("id", id);
                map.put("objectName", objectName);
                map.put("bucketName", bucketName);
            }
        }
        return map;
    }

    private String insertUploadFile(String bucketName, String fileName, String md5, String objectName) {
        UploadFileEntity insertEntity = new UploadFileEntity();
        String id = IDUtil.id();
        insertEntity.setId(id);
        insertEntity.setFileName(fileName);
        insertEntity.setMd5(md5);
        insertEntity.setBucketName(bucketName);
        insertEntity.setSavePath(objectName);
        insertEntity.setTime();
        int count = uploadChunkMapper.insert(insertEntity);
        if (count > 0) {
            return id;
        }
        return null;
    }

    /**
     * 单文件上传（不分片）
     *
     * @param uploadFileDTO
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> uploadBySingle(UploadFileDTO uploadFileDTO) throws Exception {
        Map<String, Object> map = Maps.newHashMap();
        String bucketName = uploadFileDTO.getBucketName();
        String md5 = uploadFileDTO.getMd5();
        MultipartFile file = uploadFileDTO.getFile();
        String fileName = uploadFileDTO.getFileName();
        String objectName = minioUtil.uploadSingle(bucketName, file);
        // 插入新的记录
        return getStringObjectMap(map, md5, fileName, objectName, bucketName);
    }

    /**
     * 文件秒传
     *
     * @param uploadFileDTO
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> uploadByInstant(UploadFileDTO uploadFileDTO) throws Exception {
        Map<String, Object> map = Maps.newHashMap();
        String md5 = uploadFileDTO.getMd5();
        String fileName = uploadFileDTO.getFileName();
        UploadFileEntity entity = uploadChunkMapper.selectOne(new QueryWrapper<UploadFileEntity>().eq("md5", md5), false);
        if (null == entity) return map;
        String bucketName = entity.getBucketName();
        String objectName = entity.getSavePath();
        if (!bucketName.equals(LINK_FILE_PATH)) {
            // 文件已存在，并且文件没有在公共的链接文件目录下，需要移动文件到公共链接文件目录下
            objectName = minioUtil.moveLinkFilePath(entity.getBucketName(), entity.getSavePath(), fileName);
            // 更新已存在文件的路径地址
            entity.setSavePath(objectName);
            entity.setBucketName(LINK_FILE_PATH);
            entity.setUpdateTime();
            uploadChunkMapper.updateById(entity);
        }
        // 插入新的记录
        return getStringObjectMap(map, md5, fileName, objectName, LINK_FILE_PATH);
    }

    private Map<String, Object> getStringObjectMap(Map<String, Object> map, String md5, String fileName, String objectName, String bucketName) {
        String id = insertUploadFile(bucketName, fileName, md5, objectName);
        if (id != null) {
            map.put("id", id);
            map.put("objectName", objectName);
            map.put("bucketName", bucketName);
        }
        return map;
    }

    @Override
    public String getFileViewUrl(String id) {
        UploadFileEntity entity = uploadChunkMapper.selectById(id);
        if (null == entity) return null;
        return minioUtil.preview(entity.getBucketName(), entity.getSavePath());
    }

    /**
     * 根据ID删除文件
     *
     * @param id
     * @return
     */
    @Override
    public Boolean deleteFileById(Long id) {
        UploadFileEntity entity = uploadChunkMapper.selectById(id);
        if (entity != null) {
            if (!entity.getBucketName().equals(LINK_FILE_PATH)) {
                // 不是公共文件，则执行文件删除
                minioUtil.remove(entity.getBucketName(), entity.getSavePath());
            }
            int count = uploadChunkMapper.deleteById(entity.getId());
            return count > 0;
        }
        return false;
    }
}
